layui.define('layer', function (exports) {
    "use strict";

    var $ = layui.$
        , layer = layui.layer
        , hint = layui.hint()
        , device = layui.device()

        //外部接口
        , upload = {
            config: {} //全局配置项

            //设置全局项
            , set: function (options) {
                var that = this;
                that.config = $.extend({}, that.config, options);
                return that;
            }

            //事件监听
            , on: function (events, callback) {
                return layui.onevent.call(this, MOD_NAME, events, callback);
            }
        }

        //操作当前实例
        , thisUpload = function () {
            var that = this;
            return {
                upload: function (files) {
                    that.upload.call(that, files);
                }
                , config: that.config
            }
        }

        //字符常量
        , MOD_NAME = 'upload', ELEM = '.layui-upload', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide',
        DISABLED = 'layui-disabled'

        , ELEM_FILE = 'layui-upload-file', ELEM_FORM = 'layui-upload-form', ELEM_IFRAME = 'layui-upload-iframe',
        ELEM_CHOOSE = 'layui-upload-choose', ELEM_DRAG = 'layui-upload-drag'


        //构造器
        , Class = function (options) {
            var that = this;
            that.config = $.extend({}, that.config, upload.config, options);
            that.render();
        };

    //默认配置
    Class.prototype.config = {
        accept: 'images' //允许上传的文件类型：images/file/video/audio
        , exts: '' //允许上传的文件后缀名
        , auto: true //是否选完文件后自动上传
        , bindAction: '' //手动上传触发的元素
        , url: '' //上传地址
        , field: 'file' //文件字段名
        , method: 'post' //请求上传的http类型
        , data: {} //请求上传的额外参数
        , drag: true //是否允许拖拽上传
        , size: 0 //文件限制大小，默认不限制
        , number: 0 //允许同时上传的文件数，默认不限制
        , multiple: false //是否允许多文件上传，不支持ie8-9
    };

    //初始渲染
    Class.prototype.render = function (options) {
        var that = this
            , options = that.config;

        options.elem = $(options.elem);
        options.bindAction = $(options.bindAction);

        that.file();
        that.events();
    };

    //追加文件域
    Class.prototype.file = function () {
        var that = this
            , options = that.config
            , elemFile = that.elemFile = $([
            '<input class="' + ELEM_FILE + '" type="file" accept="' + options.acceptMime + '" name="' + options.field + '"'
            , (options.multiple ? ' multiple' : '')
            , '>'
        ].join(''))
            , next = options.elem.next();

        if (next.hasClass(ELEM_FILE) || next.hasClass(ELEM_FORM)) {
            next.remove();
        }

        //包裹ie8/9容器
        if (device.ie && device.ie < 10) {
            options.elem.wrap('<div class="layui-upload-wrap"></div>');
        }

        that.isFile() ? (
            that.elemFile = options.elem
                , options.field = options.elem[0].name
        ) : options.elem.after(elemFile);

        //初始化ie8/9的Form域
        if (device.ie && device.ie < 10) {
            that.initIE();
        }
    };

    //ie8-9初始化
    Class.prototype.initIE = function () {
        var that = this
            , options = that.config
            ,
            iframe = $('<iframe id="' + ELEM_IFRAME + '" class="' + ELEM_IFRAME + '" name="' + ELEM_IFRAME + '" frameborder="0"></iframe>')
            , elemForm = $(['<form target="' + ELEM_IFRAME + '" class="' + ELEM_FORM + '" method="' + options.method
                , '" key="set-mine" enctype="multipart/form-data" action="' + options.url + '">'
                , '</form>'].join(''));

        //插入iframe
        $('#' + ELEM_IFRAME)[0] || $('body').append(iframe);

        //包裹文件域
        if (!options.elem.next().hasClass(ELEM_IFRAME)) {
            that.elemFile.wrap(elemForm);

            //追加额外的参数
            options.elem.next('.' + ELEM_IFRAME).append(function () {
                var arr = [];
                layui.each(options.data, function (key, value) {
                    value = typeof value === 'function' ? value() : value;
                    arr.push('<input type="hidden" name="' + key + '" value="' + value + '">')
                });
                return arr.join('');
            }());
        }
    };

    //异常提示
    Class.prototype.msg = function (content) {
        return layer.msg(content, {
            icon: 2
            , shift: 6
        });
    };

    //判断绑定元素是否为文件域本身
    Class.prototype.isFile = function () {
        var elem = this.config.elem[0];
        if (!elem) return;
        return elem.tagName.toLocaleLowerCase() === 'input' && elem.type === 'file'
    }

    //预读图片信息
    Class.prototype.preview = function (callback) {
        var that = this;
        if (window.FileReader) {
            layui.each(that.chooseFiles, function (index, file) {
                var reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = function () {
                    callback && callback(index, file, this.result);
                }
            });
        }
    };

    //执行上传
    Class.prototype.upload = function (files, type) {
        var that = this
            , options = that.config
            , elemFile = that.elemFile[0]

            //高级浏览器处理方式，支持跨域
            , ajaxSend = function () {
                var successful = 0, aborted = 0
                    , items = files || that.files || that.chooseFiles || elemFile.files
                    , allDone = function () { //多文件全部上传完毕的回调
                    if (options.multiple && successful + aborted === that.fileLength) {
                        typeof options.allDone === 'function' && options.allDone({
                            total: that.fileLength
                            , successful: successful
                            , aborted: aborted
                        });
                    }
                };
                //--------------ajax多附件上传--------------
                var xhrOnProgress = function (fun) {
                    xhrOnProgress.onprogress = fun; //绑定监听
                    //使用闭包实现监听绑
                    return function () {
                        //通过$.ajaxSettings.xhr();获得XMLHttpRequest对象
                        var xhr = $.ajaxSettings.xhr();
                        //判断监听函数是否为函数
                        if (typeof xhrOnProgress.onprogress !== 'function')
                            return xhr;
                        //如果有监听函数并且xhr对象支持绑定时就把监听函数绑定上去
                        if (xhrOnProgress.onprogress && xhr.upload) {
                            xhr.upload.onprogress = xhrOnProgress.onprogress;
                        }
                        return xhr;
                    }
                }
                //--------------ajax多附件上传--------------
                layui.each(items, function (index, file) {
                    var formData = new FormData();

                    formData.append(options.field, file);

                    //追加额外的参数
                    layui.each(options.data, function (key, value) {
                        value = typeof value === 'function' ? value() : value;
                        formData.append(key, value);
                    });

                    //提交文件
                    $.ajax({
                        url: options.url
                        , type: options.method
                        , data: formData
                        , contentType: false
                        , processData: false
                        , dataType: 'json'
                        , headers: options.headers || {}
                        , success: function (res) {
                            successful++;
                            done(index, res);
                            allDone();
                        }
                        , error: function () {
                            aborted++;
                            that.msg('请求上传接口出现异常');
                            error(index);
                            allDone();
                        },
                        //--------------ajax多附件上传--------------
                        xhr: xhrOnProgress(function (e) {
                            //var percent=e.loaded / e.total;//计算百分比
                            //console.log(percent);
                            xhr(index, e);
                        })
                        //--------------ajax多附件上传--------------
                    });
                });
            }

            //低版本IE处理方式，不支持跨域
            , iframeSend = function () {
                var iframe = $('#' + ELEM_IFRAME);

                that.elemFile.parent().submit();

                //获取响应信息
                clearInterval(Class.timer);
                Class.timer = setInterval(function () {
                    var res, iframeBody = iframe.contents().find('body');
                    try {
                        res = iframeBody.text();
                    } catch (e) {
                        that.msg('获取上传后的响应信息出现异常');
                        clearInterval(Class.timer);
                        error();
                    }
                    if (res) {
                        clearInterval(Class.timer);
                        iframeBody.html('');
                        done(0, res);
                    }
                }, 30);
            }

            //统一回调
            , done = function (index, res) {
                that.elemFile.next('.' + ELEM_CHOOSE).remove();
                elemFile.value = '';
                if (typeof res !== 'object') {
                    try {
                        res = JSON.parse(res);
                    } catch (e) {
                        res = {};
                        return that.msg('请对上传接口返回有效JSON');
                    }
                }
                typeof options.done === 'function' && options.done(res, index || 0, function (files) {
                    that.upload(files);
                });
            }
            //--------------ajax多附件上传--------------
            , xhr = function (index, e) {
                typeof options.xhr === 'function' && options.xhr(index || 0, e);
            }
            //--------------ajax多附件上传--------------

            //统一网络异常回调
            , error = function (index) {
                if (options.auto) {
                    elemFile.value = '';
                }
                typeof options.error === 'function' && options.error(index || 0, function (files) {
                    that.upload(files);
                });
            }

            , exts = options.exts
            , check, value = function () {
                var arr = [];
                layui.each(files || that.chooseFiles, function (i, item) {
                    arr.push(item.name);
                });
                return arr;
            }()

            //回调返回的参数
            , args = {
                preview: function (callback) {
                    that.preview(callback);
                }
                , upload: function (index, file) {
                    var thisFile = {};
                    thisFile[index] = file;
                    that.upload(thisFile);
                }
                , pushFile: function () {
                    that.files = that.files || {};
                    layui.each(that.chooseFiles, function (index, item) {
                        that.files[index] = item;
                    });
                    return that.files;
                }
            }

            //提交上传
            , send = function () {
                if (type === 'choose') {
                    return options.choose && options.choose(args);
                }

                //上传前的回调
                options.before && options.before(args);

                //IE兼容处理
                if (device.ie) {
                    return device.ie > 9 ? ajaxSend() : iframeSend();
                }

                ajaxSend();
            }

        //校验文件格式
        value = value.length === 0
            ? ((elemFile.value.match(/[^\/\\]+\..+/g) || []) || '')
            : value;

        if (value.length === 0) return;

        switch (options.accept) {
            case 'file': //一般文件
                if (exts && !RegExp('\\w\\.(' + exts + ')$', 'i').test(escape(value))) {
                    that.msg('选择的文件中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
            case 'video': //视频文件
                if (!RegExp('\\w\\.(' + (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') + ')$', 'i').test(escape(value))) {
                    that.msg('选择的视频中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
            case 'audio': //音频文件
                if (!RegExp('\\w\\.(' + (exts || 'mp3|wav|mid') + ')$', 'i').test(escape(value))) {
                    that.msg('选择的音频中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
            default: //图片文件
                layui.each(value, function (i, item) {
                    if (!RegExp('\\w\\.(' + (exts || 'jpg|png|gif|bmp|jpeg$') + ')', 'i').test(escape(item))) {
                        check = true;
                    }
                });
                if (check) {
                    that.msg('选择的图片中包含不支持的格式');
                    return elemFile.value = '';
                }
                break;
        }

        //检验文件数量
        that.fileLength = function () {
            var length = 0
                , items = files || that.files || that.chooseFiles || elemFile.files;
            layui.each(items, function () {
                length++;
            });
            return length;
        }();
        if (options.number && that.fileLength > options.number) {
            return that.msg('同时最多只能上传的数量为：' + options.number);
        }

        //检验文件大小
        if (options.size > 0 && !(device.ie && device.ie < 10)) {
            var limitSize;
            layui.each(that.chooseFiles, function (index, file) {
                if (file.size > 1024 * options.size) {
                    var size = options.size / 1024;
                    size = size >= 1
                        ? (Math.floor(size) + (size % 1 > 0 ? size.toFixed(1) : 0)) + 'MB'
                        : options.size + 'KB'
                    elemFile.value = '';
                    limitSize = size;

                }
            });
            if (limitSize) return that.msg('文件不能超过' + limitSize + ",您可以试试右边的分片上传哦！");
        }
        send();
    };

    //事件处理
    Class.prototype.events = function () {
        var that = this
            , options = that.config

            //设置当前选择的文件队列
            , setChooseFile = function (files) {
                that.chooseFiles = {};
                layui.each(files, function (i, item) {
                    var time = new Date().getTime();
                    that.chooseFiles[time + '-' + i] = item;
                });
            }

            //设置选择的文本
            , setChooseText = function (files, filename) {
                var elemFile = that.elemFile
                    , value = files.length > 1
                    ? files.length + '个文件'
                    : ((files[0] || {}).name || (elemFile[0].value.match(/[^\/\\]+\..+/g) || []) || '');

                if (elemFile.next().hasClass(ELEM_CHOOSE)) {
                    elemFile.next().remove();
                }
                that.upload(null, 'choose');
                if (that.isFile() || options.choose) return;
                elemFile.after('<span class="layui-inline ' + ELEM_CHOOSE + '">' + value + '</span>');
            };

        //点击上传容器
        options.elem.off('upload.start').on('upload.start', function () {
            var othis = $(this), data = othis.attr('lay-data');

            if (data) {
                try {
                    data = new Function('return ' + data)();
                    that.config = $.extend({}, options, data);
                } catch (e) {
                    hint.error('Upload element property lay-data configuration item has a syntax error: ' + data)
                }
            }

            that.config.item = othis;
            that.elemFile[0].click();
        });

        //拖拽上传
        if (!(device.ie && device.ie < 10)) {
            options.elem.off('upload.over').on('upload.over', function () {
                var othis = $(this)
                othis.attr('lay-over', '');
            })
                .off('upload.leave').on('upload.leave', function () {
                var othis = $(this)
                othis.removeAttr('lay-over');
            })
                .off('upload.drop').on('upload.drop', function (e, param) {
                var othis = $(this), files = param.originalEvent.dataTransfer.files || [];

                othis.removeAttr('lay-over');
                setChooseFile(files);

                if (options.auto) {
                    that.upload(files);
                } else {
                    setChooseText(files);
                }
            });
        }

        //文件选择
        that.elemFile.off('upload.change').on('upload.change', function () {
            var files = this.files || [];
            setChooseFile(files);
            options.auto ? that.upload() : setChooseText(files); //是否自动触发上传
        });

        //手动触发上传
        options.bindAction.off('upload.action').on('upload.action', function () {
            that.upload();
        });

        //防止事件重复绑定
        if (options.elem.data('haveEvents')) return;

        that.elemFile.on('change', function () {
            $(this).trigger('upload.change');
        });

        options.elem.on('click', function () {
            if (that.isFile()) return;
            $(this).trigger('upload.start');
        });

        if (options.drag) {
            options.elem.on('dragover', function (e) {
                e.preventDefault();
                $(this).trigger('upload.over');
            }).on('dragleave', function (e) {
                $(this).trigger('upload.leave');
            }).on('drop', function (e) {
                e.preventDefault();
                $(this).trigger('upload.drop', e);
            });
        }

        options.bindAction.on('click', function () {
            $(this).trigger('upload.action');
        });

        options.elem.data('haveEvents', true);
    };

    //核心入口
    upload.render = function (options) {
        var inst = new Class(options);
        return thisUpload.call(inst);
    };

    exports(MOD_NAME, upload);
});
